import datetime
import functools
import json
from functools import wraps
from typing import Mapping, List, Callable

from django.conf import settings
from django.http import HttpRequest, JsonResponse
from django.shortcuts import render, redirect

from mainApp.models import RouteProtect
from utils.database.sqls import get_user_permission


def set_route_protects(minute: int = None, hour: int = None, day: int = None):
    """
    路由函数保护设置装饰器
    Example:
    @set_route_protects(minute=5)
    def my_view(request):
        return render(request, 'index.html')

    示例表示单IP每分钟最多访问5次，超过上限则重定向到警告页面，该IP这分钟内不得访问该路由
    :param minute: 分钟访问上限
    :param hour: 小时访问上限
    :param day: 日访问上限
    :return:
    """

    def decorator(func):
        @wraps(func)
        def wrapper(req: HttpRequest, *args, **kwargs):

            # 未开启路由计数保护
            if not settings.ROUTE_PROTECT or (minute is None and hour is None and day is None):
                return func(req, *args, **kwargs)

            resp = RouteProtect.objects.filter(ip_address=req.META['REMOTE_ADDR'], route_url=req.path).first()
            if resp is None:
                RouteProtect.objects.create(ip_address=req.META['REMOTE_ADDR'], per_day=1, per_minute=1,
                                            per_hour=1, route_url=req.path)
                return func(req, *args, **kwargs)
            if (minute is not None and resp.per_minute > minute) or (hour is not None and resp.per_hour > hour) or (
                    day is not None and resp.per_day > day):
                return render(req, 'route_protect.html')

            else:
                resp.per_minute += 1
                resp.per_hour += 1
                resp.per_day += 1
                resp.last_update = datetime.datetime.now()
                resp.save()

                return func(req, *args, **kwargs)

        return wrapper

    return decorator


def login_permission(permissions: List[str] = None):
    """
    权限设置装饰器
    :param permissions: 需要的权限列表
    :return:
    """

    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs):
            req: HttpRequest = args[0]
            username = req.session.get('username', None)
            # 验证登录
            if username is None:
                return redirect('mainApp:login')

            # 验证权限
            if permissions is not None:
                all_permissions = get_user_permission(username)
                for p in permissions:
                    if p not in all_permissions:
                        return render(req, 'permission_deny.html')
            return func(*args, **kwargs)

        return wrapper

    return decorator


def json_post_checker(necessary_keys: Mapping[str, type], optional_keys: Mapping[str, type] = None):
    """
    接口参数类型和传入检测
    Example:
    @json_post_checker({"name": str, "age": int})
    def view(req: HttpRequest, data: dict = None) -> HttpResponse:
        return JsonResponse()
    json解析完成并以关键字传参传入函数
    :param necessary_keys: 必传参数名和类型
    :param optional_keys: 可选参数名和类型
    :return:
    """

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            request: HttpRequest = args[0]
            if request.method != "POST":
                return JsonResponse({"code": 500, "message": "仅支持POST"})
            if request.headers.get("Content-Type") != "application/json":
                return JsonResponse({"code": 500, "message": "仅支持JSON格式"})

            data = json.loads(request.body)

            # 检测必传参数类型
            for key in necessary_keys:
                if data.get(key) is None:
                    return JsonResponse({"code": 500, "message": f"key: {key} lost"})
                if data.get(key).__class__ != necessary_keys[key]:
                    return JsonResponse({"code": 500,
                                         "message":
                                             f"key: {key} supposed '{necessary_keys[key].__name__}' type, but got '"
                                             f"{data.get(key).__class__.__name__}'"})
            # 检测可选参数类型
            if optional_keys is not None:
                for key in optional_keys:
                    if data.get(key) is None:
                        continue

                    if data.get(key).__class__ != optional_keys[key]:
                        return JsonResponse({"code": 500,
                                             "message":
                                                 f"key: {key} supposed '{necessary_keys[key].__name__}' type, but got '"
                                                 f"{data.get(key).__class__.__name__}'"})
            return func(*args, data=data, **kwargs)

        return wrapper

    return decorator
